1 module hip.game2d.tileworld;
2 public import hip.api.data.tilemap;
3 public import hip.component.physics;
4 public import hip.math.collision;
5 
6 
7 
8 class TileWorld
9 {
10     IHipTilemap map;
11     ///Affects position
12     float constantGravity = 0;
13     ///Affects velocity
14     float gravity = 0;
15 
16     BodyRectComponent[] dynamicBodies;
17     HipTileLayer[] collidibleLayers;
18 
19     this(IHipTilemap map, float gravity = 0, float constantGravity = 0)
20     {
21         this.map = map;
22         this.gravity = gravity;
23         this.constantGravity = constantGravity;
24     }
25 
26     private Rect[128] rectCache;
27     Rect[] getRectsOverlapping(HipTileLayer layer, in Rect input) @nogc
28     {
29         import hip.math.utils;
30         if(!layer.isInLayerBoundaries(
31             cast(int)input.x, cast(int)input.y, cast(int)input.w, cast(int)input.h
32         ))
33             return rectCache[0..0];
34         
35         uint tileW = map.tileWidth, tileH = map.tileHeight;
36         float x = input.x - layer.x;
37         float y = input.y - layer.y;
38         
39 
40 
41         
42 
43         float inputTilesWidth = input.w / tileW;
44         float inputTilesHeight = input.h / tileH;
45 
46         float i = x/tileW;
47         float j = y/tileH;
48         float i2 = ceil(min(i + inputTilesWidth, layer.columns));
49         float j2 = ceil(min(j + inputTilesHeight, layer.rows - 1));
50 
51 
52         float plusI = min(inputTilesWidth, 1);
53         float plusJ = min(inputTilesHeight, 1);
54 
55         int lastI = -1;
56         int lastJ = -1;
57 
58         int rects = 0;
59         for(; j < j2; j+= plusJ)
60         for(float _i = i; _i < i2; _i+= plusI)
61         {
62             if(_i < 0 || j < 0)
63                 continue;
64             int ui = cast(int)_i;
65             int uj = cast(int)j;
66 
67             if(ui == lastI && uj == lastJ)
68                 continue;
69             lastI = ui;
70             lastJ = uj;
71 
72             ushort tID = layer.checkedGetTile(ui, uj);
73             if(tID != 0)
74             {
75                 rectCache[rects++] = Rect(layer.x + ui*tileW, layer.y + uj*tileH, tileW, tileH);
76             }
77         }
78         return rectCache[0..rects];
79     }
80 
81     void addDynamic(IComponentizable component)
82     {
83         auto comp = component.getComponent!BodyRectComponent;
84         assert(comp !is null, "No BodyRectComponent found");
85         assert(comp.size.w != 0, "Can't have 0 width component");
86         assert(comp.size.h != 0, "Can't have 0 height component");
87         dynamicBodies~= comp;
88     }
89 
90     void addCollidibleLayer(HipTileLayer layer)
91     {
92         collidibleLayers~= layer;
93     }
94 
95     void update2(float dt) @nogc
96     {
97         import std.algorithm.sorting:sort;
98         struct DynamicRectCollision
99         {
100             Vector2 normal;
101             float time;
102         }
103         static DynamicRectCollision[128] colCache;
104         foreach(dynBody; dynamicBodies)
105         {
106             dynBody.velocity.y += gravity;
107             Rect bodyRec = dynBody.rect;
108             int z = 0;
109 
110             foreach(HipTileLayer l; collidibleLayers) 
111             for(int j = 0; j < l.rows; j++)
112             for(int i = 0; i < l.columns; i++)
113             {
114                 if(l.getTile(i, j) != 0)
115                 {
116                     DynamicRectCollision col = void;
117                     if(isDynamicRectOverlappingRect(bodyRec, dynBody.velocity, Rect(l.x + i*l.tileWidth, l.y+j*l.tileHeight, l.tileWidth, l.tileHeight), dt, col.normal, col.time))
118                         colCache[z++] = col;
119                 }
120             }
121             if(z > 0)
122             foreach(col; sort!((DynamicRectCollision a, DynamicRectCollision b) => a.time < b.time)(colCache[0..z]))
123                 resolveDynamicRectOverlappingRect(col.normal, dynBody.velocity, col.time);
124             dynBody.position+= dynBody.velocity* dt;
125         }
126     }
127 
128     void update(float dt) @nogc
129     {
130         import std.algorithm.sorting:sort;
131         struct DynamicRectCollision
132         {
133             Vector2 normal;
134             float time;
135         }
136         static DynamicRectCollision[128] colCache;
137         foreach(dynBody; dynamicBodies)
138         {
139             dynBody.velocity.y += gravity;
140             Rect bodyRec = dynBody.rect;
141             int i = 0;
142 
143             foreach(l; collidibleLayers) 
144             foreach(const ref rect; getRectsOverlapping(l, dynBody.expandedRectVel))
145             {
146                 DynamicRectCollision col = void;
147                 if(isDynamicRectOverlappingRect(bodyRec, dynBody.velocity, rect, dt, col.normal, col.time))
148                     colCache[i++] = col;
149             }
150             if(i > 0) 
151             foreach(col; sort!((DynamicRectCollision a, DynamicRectCollision b) => a.time < b.time)(colCache[0..i]))
152                 resolveDynamicRectOverlappingRect(col.normal, dynBody.velocity, col.time);
153             dynBody.position+= dynBody.velocity* dt;
154         }
155     }
156 }